1 module hip.audio_decoding.resampler;
2 
3 version(none): //Currently unused as audio players already resample.
4 
5 /* Copyright (C) 2007-2008 Jean-Marc Valin
6     * Copyright (C) 2008      Thorvald Natvig
7     * D port by Ketmar // Invisible Vector
8     * D Source took from ARSD codebase
9     *
10     * Arbitrary resampling code
11     *
12     * Redistribution and use in source and binary forms, with or without
13     * modification, are permitted provided that the following conditions are
14     * met:
15     *
16     * 1. Redistributions of source code must retain the above copyright notice,
17     * this list of conditions and the following disclaimer.
18     *
19     * 2. Redistributions in binary form must reproduce the above copyright
20     * notice, this list of conditions and the following disclaimer in the
21     * documentation and/or other materials provided with the distribution.
22     *
23     * 3. The name of the author may not be used to endorse or promote products
24     * derived from this software without specific prior written permission.
25     *
26     * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27     * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28     * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
29     * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
30     * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
31     * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
32     * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34     * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
35     * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36     * POSSIBILITY OF SUCH DAMAGE.
37     */
38 
39 /* A-a-a-and now... D port is covered by the following license!
40     *
41     * This program is free software: you can redistribute it and/or modify
42     * it under the terms of the GNU General Public License as published by
43     * the Free Software Foundation, either version 3 of the License, or
44     * (at your option) any later version.
45     *
46     * This program is distributed in the hope that it will be useful,
47     * but WITHOUT ANY WARRANTY; without even the implied warranty of
48     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
49     * GNU General Public License for more details.
50     *
51     * You should have received a copy of the GNU General Public License
52     * along with this program. If not, see <http://www.gnu.org/licenses/>.
53     */
54 //module iv.follin.resampler /*is aliced*/;
55 //import iv.alice;
56 
57 /*
58     The design goals of this code are:
59         - Very fast algorithm
60         - SIMD-friendly algorithm
61         - Low memory requirement
62         - Good *perceptual* quality (and not best SNR)
63     Warning: This resampler is relatively new. Although I think I got rid of
64     all the major bugs and I don't expect the API to change anymore, there
65     may be something I've missed. So use with caution.
66     This algorithm is based on this original resampling algorithm:
67     Smith, Julius O. Digital Audio Resampling Home Page
68     Center for Computer Research in Music and Acoustics (CCRMA),
69     Stanford University, 2007.
70     Web published at http://www-ccrma.stanford.edu/~jos/resample/.
71     There is one main difference, though. This resampler uses cubic
72     interpolation instead of linear interpolation in the above paper. This
73     makes the table much smaller and makes it possible to compute that table
74     on a per-stream basis. In turn, being able to tweak the table for each
75     stream makes it possible to both reduce complexity on simple ratios
76     (e.g. 2/3), and get rid of the rounding operations in the inner loop.
77     The latter both reduces CPU time and makes the algorithm more SIMD-friendly.
78 */
79 version = sincresample_use_full_table;
80 version(X86) {
81     version(sincresample_disable_sse) {
82     } else {
83     version(D_PIC) {} else version = sincresample_use_sse;
84     }
85 }
86 
87 
88 // ////////////////////////////////////////////////////////////////////////// //
89 public struct SpeexResampler {
90 public:
91     alias Quality = int;
92     enum : uint {
93     Fastest = 0,
94     Voip = 3,
95     Default = 4,
96     Desktop = 5,
97     Music = 8,
98     Best = 10,
99     }
100 
101     enum Error {
102     OK = 0,
103     NoMemory,
104     BadState,
105     BadArgument,
106     BadData,
107     }
108 
109 private:
110 nothrow @trusted @nogc:
111     alias ResamplerFn = int function (ref SpeexResampler st, uint chanIdx, const(float)* indata, uint *indataLen, float *outdata, uint *outdataLen);
112 
113 private:
114     uint inRate;
115     uint outRate;
116     uint numRate; // from
117     uint denRate; // to
118 
119     Quality srQuality;
120     uint chanCount;
121     uint filterLen;
122     uint memAllocSize;
123     uint bufferSize;
124     int intAdvance;
125     int fracAdvance;
126     float cutoff;
127     uint oversample;
128     bool started;
129 
130     // these are per-channel
131     int[64] lastSample;
132     uint[64] sampFracNum;
133     uint[64] magicSamples;
134 
135     float* mem;
136     uint realMemLen; // how much memory really allocated
137     float* sincTable;
138     uint sincTableLen;
139     uint realSincTableLen; // how much memory really allocated
140     ResamplerFn resampler;
141 
142     int inStride;
143     int outStride;
144 
145 public:
146     static string errorStr (int err) {
147     switch (err) with (Error) {
148         case OK: return "success";
149         case NoMemory: return "memory allocation failed";
150         case BadState: return "bad resampler state";
151         case BadArgument: return "invalid argument";
152         case BadData: return "bad data passed";
153         default:
154     }
155     return "unknown error";
156     }
157 
158 public:
159     @disable this (this);
160     ~this () { deinit(); }
161 
162     @property bool inited () const pure { return (resampler !is null); }
163 
164     void deinit () {
165     import core.stdc.stdlib : free;
166     if (mem !is null) { free(mem); mem = null; }
167     if (sincTable !is null) { free(sincTable); sincTable = null; }
168     /*
169     memAllocSize = realMemLen = 0;
170     sincTableLen = realSincTableLen = 0;
171     resampler = null;
172     started = false;
173     */
174     inRate = outRate = numRate = denRate = 0;
175     srQuality = cast(Quality)666;
176     chanCount = 0;
177     filterLen = 0;
178     memAllocSize = 0;
179     bufferSize = 0;
180     intAdvance = 0;
181     fracAdvance = 0;
182     cutoff = 0;
183     oversample = 0;
184     started = 0;
185 
186     mem = null;
187     realMemLen = 0; // how much memory really allocated
188     sincTable = null;
189     sincTableLen = 0;
190     realSincTableLen = 0; // how much memory really allocated
191     resampler = null;
192 
193     inStride = outStride = 0;
194     }
195 
196     /** Create a new resampler with integer input and output rates.
197     *
198     * Params:
199     *  chans = Number of channels to be processed
200     *  inRate = Input sampling rate (integer number of Hz).
201     *  outRate = Output sampling rate (integer number of Hz).
202     *  aquality = Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
203     *
204     * Returns:
205     *  0 or error code
206     */
207     Error setup (uint chans, uint ainRate, uint aoutRate, Quality aquality/*, usize line=__LINE__*/) {
208     //{ import core.stdc.stdio; printf("init: %u -> %u at %u\n", ainRate, aoutRate, cast(uint)line); }
209     import core.stdc.stdlib : malloc, free;
210 
211     deinit();
212     if (aquality < 0) aquality = 0;
213     if (aquality > SpeexResampler.Best) aquality = SpeexResampler.Best;
214     if (chans < 1 || chans > 16) return Error.BadArgument;
215 
216     started = false;
217     inRate = 0;
218     outRate = 0;
219     numRate = 0;
220     denRate = 0;
221     srQuality = cast(Quality)666; // it's ok
222     sincTableLen = 0;
223     memAllocSize = 0;
224     filterLen = 0;
225     mem = null;
226     resampler = null;
227 
228     cutoff = 1.0f;
229     chanCount = chans;
230     inStride = 1;
231     outStride = 1;
232 
233     bufferSize = 160;
234 
235     // per channel data
236     lastSample[] = 0;
237     magicSamples[] = 0;
238     sampFracNum[] = 0;
239 
240     setQuality(aquality);
241     setRate(ainRate, aoutRate);
242 
243     if (auto filterErr = updateFilter()) { deinit(); return filterErr; }
244     skipZeros(); // make sure that the first samples to go out of the resamplers don't have leading zeros
245 
246     return Error.OK;
247     }
248 
249     /** Set (change) the input/output sampling rates (integer value).
250     *
251     * Params:
252     *  ainRate = Input sampling rate (integer number of Hz).
253     *  aoutRate = Output sampling rate (integer number of Hz).
254     *
255     * Returns:
256     *  0 or error code
257     */
258     Error setRate (uint ainRate, uint aoutRate/*, usize line=__LINE__*/) {
259     //{ import core.stdc.stdio; printf("changing rate: %u -> %u at %u\n", ainRate, aoutRate, cast(uint)line); }
260     if (inRate == ainRate && outRate == aoutRate) return Error.OK;
261     //{ import core.stdc.stdio; printf("changing rate: %u -> %u at %u\n", ratioNum, ratioDen, cast(uint)line); }
262 
263     uint oldDen = denRate;
264     inRate = ainRate;
265     outRate = aoutRate;
266     auto div = gcd(ainRate, aoutRate);
267     numRate = ainRate/div;
268     denRate = aoutRate/div;
269 
270     if (oldDen > 0) {
271         foreach (ref v; sampFracNum.ptr[0..chanCount]) {
272     v = v*denRate/oldDen;
273     // safety net
274     if (v >= denRate) v = denRate-1;
275         }
276     }
277 
278     return (inited ? updateFilter() : Error.OK);
279     }
280 
281     /** Get the current input/output sampling rates (integer value).
282     *
283     * Params:
284     *  ainRate = Input sampling rate (integer number of Hz) copied.
285     *  aoutRate = Output sampling rate (integer number of Hz) copied.
286     */
287     void getRate (out uint ainRate, out uint aoutRate) {
288     ainRate = inRate;
289     aoutRate = outRate;
290     }
291 
292     @property uint getInRate () { return inRate; }
293     @property uint getOutRate () { return outRate; }
294 
295     @property uint getChans () { return chanCount; }
296 
297     /** Get the current resampling ratio. This will be reduced to the least common denominator.
298     *
299     * Params:
300     *  ratioNum = Numerator of the sampling rate ratio copied
301     *  ratioDen = Denominator of the sampling rate ratio copied
302     */
303     void getRatio (out uint ratioNum, out uint ratioDen) {
304     ratioNum = numRate;
305     ratioDen = denRate;
306     }
307 
308     /** Set (change) the conversion quality.
309     *
310     * Params:
311     *  quality = Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
312     *
313     * Returns:
314     *  0 or error code
315     */
316     Error setQuality (Quality aquality) {
317     if (aquality < 0) aquality = 0;
318     if (aquality > SpeexResampler.Best) aquality = SpeexResampler.Best;
319     if (srQuality == aquality) return Error.OK;
320     srQuality = aquality;
321     return (inited ? updateFilter() : Error.OK);
322     }
323 
324     /** Get the conversion quality.
325     *
326     * Returns:
327     *  Resampling quality between 0 and 10, where 0 has poor quality and 10 has very high quality.
328     */
329     int getQuality () { return srQuality; }
330 
331     /** Get the latency introduced by the resampler measured in input samples.
332     *
333     * Returns:
334     *  Input latency;
335     */
336     int inputLatency () { return filterLen/2; }
337 
338     /** Get the latency introduced by the resampler measured in output samples.
339     *
340     * Returns:
341     *  Output latency.
342     */
343     int outputLatency () { return ((filterLen/2)*denRate+(numRate>>1))/numRate; }
344 
345     /* Make sure that the first samples to go out of the resamplers don't have
346     * leading zeros. This is only useful before starting to use a newly created
347     * resampler. It is recommended to use that when resampling an audio file, as
348     * it will generate a file with the same length. For real-time processing,
349     * it is probably easier not to use this call (so that the output duration
350     * is the same for the first frame).
351     *
352     * Setup/reset sequence will automatically call this, so it is private.
353     */
354     private void skipZeros () { foreach (immutable i; 0..chanCount) lastSample.ptr[i] = filterLen/2; }
355 
356     static struct Data {
357     const(float)[] dataIn;
358     float[] dataOut;
359     uint inputSamplesUsed; // out value, in samples (i.e. multiplied by channel count)
360     uint outputSamplesUsed; // out value, in samples (i.e. multiplied by channel count)
361     }
362 
363     /** Resample (an interleaved) float array. The input and output buffers must *not* overlap.
364     * `data.dataIn` can be empty, but `data.dataOut` can't.
365     * Function will return number of consumed samples (*not* *frames*!) in `data.inputSamplesUsed`,
366     * and number of produced samples in `data.outputSamplesUsed`.
367     * You should provide enough samples for all channels, and all channels will be processed.
368     *
369     * Params:
370     *  data = input and output buffers, number of frames consumed and produced
371     *
372     * Returns:
373     *  0 or error code
374     */
375     Error process(string mode="interleaved") (ref Data data) {
376     static assert(mode == "interleaved" || mode == "sequential");
377 
378     data.inputSamplesUsed = data.outputSamplesUsed = 0;
379     if (!inited) return Error.BadState;
380 
381     if (data.dataIn.length%chanCount || data.dataOut.length < 1 || data.dataOut.length%chanCount) return Error.BadData;
382     if (data.dataIn.length > uint.max/4 || data.dataOut.length > uint.max/4) return Error.BadData;
383 
384     static if (mode == "interleaved") {
385         inStride = outStride = chanCount;
386     } else {
387         inStride = outStride = 1;
388     }
389     uint iofs = 0, oofs = 0;
390     immutable uint idclen = cast(uint)(data.dataIn.length/chanCount);
391     immutable uint odclen = cast(uint)(data.dataOut.length/chanCount);
392     foreach (immutable i; 0..chanCount) {
393         data.inputSamplesUsed = idclen;
394         data.outputSamplesUsed = odclen;
395         if (data.dataIn.length) {
396     processOneChannel(i, data.dataIn.ptr+iofs, &data.inputSamplesUsed, data.dataOut.ptr+oofs, &data.outputSamplesUsed);
397         } else {
398     processOneChannel(i, null, &data.inputSamplesUsed, data.dataOut.ptr+oofs, &data.outputSamplesUsed);
399         }
400         static if (mode == "interleaved") {
401     ++iofs;
402     ++oofs;
403         } else {
404     iofs += idclen;
405     oofs += odclen;
406         }
407     }
408     data.inputSamplesUsed *= chanCount;
409     data.outputSamplesUsed *= chanCount;
410     return Error.OK;
411     }
412 
413 
414     //HACK for libswresample
415     // return -1 or number of outframes
416     int swrconvert (float** outbuf, int outframes, const(float)**inbuf, int inframes) {
417     if (!inited || outframes < 1 || inframes < 0) return -1;
418     inStride = outStride = 1;
419     Data data;
420     foreach (immutable i; 0..chanCount) {
421         data.dataIn = (inframes ? inbuf[i][0..inframes] : null);
422         data.dataOut = (outframes ? outbuf[i][0..outframes] : null);
423         data.inputSamplesUsed = inframes;
424         data.outputSamplesUsed = outframes;
425         if (inframes > 0) {
426     processOneChannel(i, data.dataIn.ptr, &data.inputSamplesUsed, data.dataOut.ptr, &data.outputSamplesUsed);
427         } else {
428     processOneChannel(i, null, &data.inputSamplesUsed, data.dataOut.ptr, &data.outputSamplesUsed);
429         }
430     }
431     return data.outputSamplesUsed;
432     }
433 
434     /// Reset a resampler so a new (unrelated) stream can be processed.
435     void reset () {
436     lastSample[] = 0;
437     magicSamples[] = 0;
438     sampFracNum[] = 0;
439     //foreach (immutable i; 0..chanCount*(filterLen-1)) mem[i] = 0;
440     if (mem !is null) mem[0..chanCount*(filterLen-1)] = 0;
441     skipZeros(); // make sure that the first samples to go out of the resamplers don't have leading zeros
442     }
443 
444 private:
445     Error processOneChannel (uint chanIdx, const(float)* indata, uint* indataLen, float* outdata, uint* outdataLen) {
446     uint ilen = *indataLen;
447     uint olen = *outdataLen;
448     float* x = mem+chanIdx*memAllocSize;
449     immutable int filterOfs = filterLen-1;
450     immutable uint xlen = memAllocSize-filterOfs;
451     immutable int istride = inStride;
452     if (magicSamples.ptr[chanIdx]) olen -= magic(chanIdx, &outdata, olen);
453     if (!magicSamples.ptr[chanIdx]) {
454         while (ilen && olen) {
455     uint ichunk = (ilen > xlen ? xlen : ilen);
456     uint ochunk = olen;
457     if (indata !is null) {
458         //foreach (immutable j; 0..ichunk) x[j+filterOfs] = indata[j*istride];
459         if (istride == 1) {
460         x[filterOfs..filterOfs+ichunk] = indata[0..ichunk];
461         } else {
462         auto sp = indata;
463         auto dp = x+filterOfs;
464         foreach (immutable j; 0..ichunk) { *dp++ = *sp; sp += istride; }
465         }
466     } else {
467         //foreach (immutable j; 0..ichunk) x[j+filterOfs] = 0;
468         x[filterOfs..filterOfs+ichunk] = 0;
469     }
470     processNative(chanIdx, &ichunk, outdata, &ochunk);
471     ilen -= ichunk;
472     olen -= ochunk;
473     outdata += ochunk*outStride;
474     if (indata !is null) indata += ichunk*istride;
475         }
476     }
477     *indataLen -= ilen;
478     *outdataLen -= olen;
479     return Error.OK;
480     }
481 
482     Error processNative (uint chanIdx, uint* indataLen, float* outdata, uint* outdataLen) {
483     immutable N = filterLen;
484     int outSample = 0;
485     float* x = mem+chanIdx*memAllocSize;
486     uint ilen;
487     started = true;
488     // call the right resampler through the function ptr
489     outSample = resampler(this, chanIdx, x, indataLen, outdata, outdataLen);
490     if (lastSample.ptr[chanIdx] < cast(int)*indataLen) *indataLen = lastSample.ptr[chanIdx];
491     *outdataLen = outSample;
492     lastSample.ptr[chanIdx] -= *indataLen;
493     ilen = *indataLen;
494     foreach (immutable j; 0..N-1) x[j] = x[j+ilen];
495     return Error.OK;
496     }
497 
498     int magic (uint chanIdx, float **outdata, uint outdataLen) {
499     uint tempInLen = magicSamples.ptr[chanIdx];
500     float* x = mem+chanIdx*memAllocSize;
501     processNative(chanIdx, &tempInLen, *outdata, &outdataLen);
502     magicSamples.ptr[chanIdx] -= tempInLen;
503     // if we couldn't process all "magic" input samples, save the rest for next time
504     if (magicSamples.ptr[chanIdx]) {
505         immutable N = filterLen;
506         foreach (immutable i; 0..magicSamples.ptr[chanIdx]) x[N-1+i] = x[N-1+i+tempInLen];
507     }
508     *outdata += outdataLen*outStride;
509     return outdataLen;
510     }
511 
512     Error updateFilter () {
513     uint oldFilterLen = filterLen;
514     uint oldAllocSize = memAllocSize;
515     bool useDirect;
516     uint minSincTableLen;
517     uint minAllocSize;
518 
519     intAdvance = numRate/denRate;
520     fracAdvance = numRate%denRate;
521     oversample = qualityMap.ptr[srQuality].oversample;
522     filterLen = qualityMap.ptr[srQuality].baseLength;
523 
524     if (numRate > denRate) {
525         // down-sampling
526         cutoff = qualityMap.ptr[srQuality].downsampleBandwidth*denRate/numRate;
527         // FIXME: divide the numerator and denominator by a certain amount if they're too large
528         filterLen = filterLen*numRate/denRate;
529         // round up to make sure we have a multiple of 8 for SSE
530         filterLen = ((filterLen-1)&(~0x7))+8;
531         if (2*denRate < numRate) oversample >>= 1;
532         if (4*denRate < numRate) oversample >>= 1;
533         if (8*denRate < numRate) oversample >>= 1;
534         if (16*denRate < numRate) oversample >>= 1;
535         if (oversample < 1) oversample = 1;
536     } else {
537         // up-sampling
538         cutoff = qualityMap.ptr[srQuality].upsampleBandwidth;
539     }
540 
541     // choose the resampling type that requires the least amount of memory
542     version(sincresample_use_full_table) {
543         useDirect = true;
544         if (int.max/float.sizeof/denRate < filterLen) goto fail;
545     } else {
546         useDirect = (filterLen*denRate <= filterLen*oversample+8 && int.max/float.sizeof/denRate >= filterLen);
547     }
548 
549     if (useDirect) {
550         minSincTableLen = filterLen*denRate;
551     } else {
552         if ((int.max/float.sizeof-8)/oversample < filterLen) goto fail;
553         minSincTableLen = filterLen*oversample+8;
554     }
555 
556     if (sincTableLen < minSincTableLen) {
557         import core.stdc.stdlib : realloc;
558         auto nslen = cast(uint)(minSincTableLen*float.sizeof);
559         if (nslen > realSincTableLen) {
560     if (nslen < 512*1024) nslen = 512*1024; // inc to 3 mb?
561     auto x = cast(float*)realloc(sincTable, nslen);
562     if (!x) goto fail;
563     sincTable = x;
564     realSincTableLen = nslen;
565         }
566         sincTableLen = minSincTableLen;
567     }
568 
569     if (useDirect) {
570         foreach (int i; 0..denRate) {
571     foreach (int j; 0..filterLen) {
572         sincTable[i*filterLen+j] = sinc(cutoff, ((j-cast(int)filterLen/2+1)-(cast(float)i)/denRate), filterLen, qualityMap.ptr[srQuality].windowFunc);
573     }
574         }
575         if (srQuality > 8) {
576     resampler = &resamplerBasicDirect!double;
577         } else {
578     resampler = &resamplerBasicDirect!float;
579         }
580     } else {
581         foreach (immutable int i; -4..cast(int)(oversample*filterLen+4)) {
582     sincTable[i+4] = sinc(cutoff, (i/cast(float)oversample-filterLen/2), filterLen, qualityMap.ptr[srQuality].windowFunc);
583         }
584         if (srQuality > 8) {
585     resampler = &resamplerBasicInterpolate!double;
586         } else {
587     resampler = &resamplerBasicInterpolate!float;
588         }
589     }
590 
591     /* Here's the place where we update the filter memory to take into account
592         the change in filter length. It's probably the messiest part of the code
593         due to handling of lots of corner cases. */
594 
595     // adding bufferSize to filterLen won't overflow here because filterLen could be multiplied by float.sizeof above
596     minAllocSize = filterLen-1+bufferSize;
597     if (minAllocSize > memAllocSize) {
598         import core.stdc.stdlib : realloc;
599         if (int.max/float.sizeof/chanCount < minAllocSize) goto fail;
600         auto nslen = cast(uint)(chanCount*minAllocSize*mem[0].sizeof);
601         if (nslen > realMemLen) {
602     if (nslen < 16384) nslen = 16384;
603     auto x = cast(float*)realloc(mem, nslen);
604     if (x is null) goto fail;
605     mem = x;
606     realMemLen = nslen;
607         }
608         memAllocSize = minAllocSize;
609     }
610     if (!started) {
611         //foreach (i=0;i<chanCount*memAllocSize;i++) mem[i] = 0;
612         mem[0..chanCount*memAllocSize] = 0;
613     } else if (filterLen > oldFilterLen) {
614         // increase the filter length
615         foreach_reverse (uint i; 0..chanCount) {
616     uint j;
617     uint olen = oldFilterLen;
618     {
619         // try and remove the magic samples as if nothing had happened
620         //FIXME: this is wrong but for now we need it to avoid going over the array bounds
621         olen = oldFilterLen+2*magicSamples.ptr[i];
622         for (j = oldFilterLen-1+magicSamples.ptr[i]; j--; ) mem[i*memAllocSize+j+magicSamples.ptr[i]] = mem[i*oldAllocSize+j];
623         //for (j = 0; j < magicSamples.ptr[i]; ++j) mem[i*memAllocSize+j] = 0;
624         mem[i*memAllocSize..i*memAllocSize+magicSamples.ptr[i]] = 0;
625         magicSamples.ptr[i] = 0;
626     }
627     if (filterLen > olen) {
628         // if the new filter length is still bigger than the "augmented" length
629         // copy data going backward
630         for (j = 0; j < olen-1; ++j) mem[i*memAllocSize+(filterLen-2-j)] = mem[i*memAllocSize+(olen-2-j)];
631         // then put zeros for lack of anything better
632         for (; j < filterLen-1; ++j) mem[i*memAllocSize+(filterLen-2-j)] = 0;
633         // adjust lastSample
634         lastSample.ptr[i] += (filterLen-olen)/2;
635     } else {
636         // put back some of the magic!
637         magicSamples.ptr[i] = (olen-filterLen)/2;
638         for (j = 0; j < filterLen-1+magicSamples.ptr[i]; ++j) mem[i*memAllocSize+j] = mem[i*memAllocSize+j+magicSamples.ptr[i]];
639     }
640         }
641     } else if (filterLen < oldFilterLen) {
642         // reduce filter length, this a bit tricky
643         // we need to store some of the memory as "magic" samples so they can be used directly as input the next time(s)
644         foreach (immutable i; 0..chanCount) {
645     uint j;
646     uint oldMagic = magicSamples.ptr[i];
647     magicSamples.ptr[i] = (oldFilterLen-filterLen)/2;
648     // we must copy some of the memory that's no longer used
649     // copy data going backward
650     for (j = 0; j < filterLen-1+magicSamples.ptr[i]+oldMagic; ++j) {
651         mem[i*memAllocSize+j] = mem[i*memAllocSize+j+magicSamples.ptr[i]];
652     }
653     magicSamples.ptr[i] += oldMagic;
654         }
655     }
656     return Error.OK;
657 
658     fail:
659     resampler = null;
660     /* mem may still contain consumed input samples for the filter.
661         Restore filterLen so that filterLen-1 still points to the position after
662         the last of these samples. */
663     filterLen = oldFilterLen;
664     return Error.NoMemory;
665     }
666 }
667 
668 enum BUFFER_SIZE_FRAMES = 1024;//512;//2048;
669 enum BUFFER_SIZE_SHORT = BUFFER_SIZE_FRAMES * 2;
670 
671 
672 // ////////////////////////////////////////////////////////////////////////// //
673 static immutable double[68] kaiser12Table = [
674     0.99859849, 1.00000000, 0.99859849, 0.99440475, 0.98745105, 0.97779076,
675     0.96549770, 0.95066529, 0.93340547, 0.91384741, 0.89213598, 0.86843014,
676     0.84290116, 0.81573067, 0.78710866, 0.75723148, 0.72629970, 0.69451601,
677     0.66208321, 0.62920216, 0.59606986, 0.56287762, 0.52980938, 0.49704014,
678     0.46473455, 0.43304576, 0.40211431, 0.37206735, 0.34301800, 0.31506490,
679     0.28829195, 0.26276832, 0.23854851, 0.21567274, 0.19416736, 0.17404546,
680     0.15530766, 0.13794294, 0.12192957, 0.10723616, 0.09382272, 0.08164178,
681     0.07063950, 0.06075685, 0.05193064, 0.04409466, 0.03718069, 0.03111947,
682     0.02584161, 0.02127838, 0.01736250, 0.01402878, 0.01121463, 0.00886058,
683     0.00691064, 0.00531256, 0.00401805, 0.00298291, 0.00216702, 0.00153438,
684     0.00105297, 0.00069463, 0.00043489, 0.00025272, 0.00013031, 0.0000527734,
685     0.00001000, 0.00000000];
686 
687 static immutable double[36] kaiser10Table = [
688     0.99537781, 1.00000000, 0.99537781, 0.98162644, 0.95908712, 0.92831446,
689     0.89005583, 0.84522401, 0.79486424, 0.74011713, 0.68217934, 0.62226347,
690     0.56155915, 0.50119680, 0.44221549, 0.38553619, 0.33194107, 0.28205962,
691     0.23636152, 0.19515633, 0.15859932, 0.12670280, 0.09935205, 0.07632451,
692     0.05731132, 0.04193980, 0.02979584, 0.02044510, 0.01345224, 0.00839739,
693     0.00488951, 0.00257636, 0.00115101, 0.00035515, 0.00000000, 0.00000000];
694 
695 static immutable double[36] kaiser8Table = [
696     0.99635258, 1.00000000, 0.99635258, 0.98548012, 0.96759014, 0.94302200,
697     0.91223751, 0.87580811, 0.83439927, 0.78875245, 0.73966538, 0.68797126,
698     0.63451750, 0.58014482, 0.52566725, 0.47185369, 0.41941150, 0.36897272,
699     0.32108304, 0.27619388, 0.23465776, 0.19672670, 0.16255380, 0.13219758,
700     0.10562887, 0.08273982, 0.06335451, 0.04724088, 0.03412321, 0.02369490,
701     0.01563093, 0.00959968, 0.00527363, 0.00233883, 0.00050000, 0.00000000];
702 
703 static immutable double[36] kaiser6Table = [
704     0.99733006, 1.00000000, 0.99733006, 0.98935595, 0.97618418, 0.95799003,
705     0.93501423, 0.90755855, 0.87598009, 0.84068475, 0.80211977, 0.76076565,
706     0.71712752, 0.67172623, 0.62508937, 0.57774224, 0.53019925, 0.48295561,
707     0.43647969, 0.39120616, 0.34752997, 0.30580127, 0.26632152, 0.22934058,
708     0.19505503, 0.16360756, 0.13508755, 0.10953262, 0.08693120, 0.06722600,
709     0.05031820, 0.03607231, 0.02432151, 0.01487334, 0.00752000, 0.00000000];
710 
711 struct FuncDef {
712     immutable(double)* table;
713     int oversample;
714 }
715 
716 static immutable FuncDef Kaiser12 = FuncDef(kaiser12Table.ptr, 64);
717 static immutable FuncDef Kaiser10 = FuncDef(kaiser10Table.ptr, 32);
718 static immutable FuncDef Kaiser8 = FuncDef(kaiser8Table.ptr, 32);
719 static immutable FuncDef Kaiser6 = FuncDef(kaiser6Table.ptr, 32);
720 
721 
722 struct QualityMapping {
723     int baseLength;
724     int oversample;
725     float downsampleBandwidth;
726     float upsampleBandwidth;
727     immutable FuncDef* windowFunc;
728 }
729 
730 
731 /* This table maps conversion quality to internal parameters. There are two
732     reasons that explain why the up-sampling bandwidth is larger than the
733     down-sampling bandwidth:
734     1) When up-sampling, we can assume that the spectrum is already attenuated
735         close to the Nyquist rate (from an A/D or a previous resampling filter)
736     2) Any aliasing that occurs very close to the Nyquist rate will be masked
737         by the sinusoids/noise just below the Nyquist rate (guaranteed only for
738         up-sampling).
739 */
740 static immutable QualityMapping[11] qualityMap = [
741     QualityMapping(  8,  4, 0.830f, 0.860f, &Kaiser6 ), /* Q0 */
742     QualityMapping( 16,  4, 0.850f, 0.880f, &Kaiser6 ), /* Q1 */
743     QualityMapping( 32,  4, 0.882f, 0.910f, &Kaiser6 ), /* Q2 */  /* 82.3% cutoff ( ~60 dB stop) 6  */
744     QualityMapping( 48,  8, 0.895f, 0.917f, &Kaiser8 ), /* Q3 */  /* 84.9% cutoff ( ~80 dB stop) 8  */
745     QualityMapping( 64,  8, 0.921f, 0.940f, &Kaiser8 ), /* Q4 */  /* 88.7% cutoff ( ~80 dB stop) 8  */
746     QualityMapping( 80, 16, 0.922f, 0.940f, &Kaiser10), /* Q5 */  /* 89.1% cutoff (~100 dB stop) 10 */
747     QualityMapping( 96, 16, 0.940f, 0.945f, &Kaiser10), /* Q6 */  /* 91.5% cutoff (~100 dB stop) 10 */
748     QualityMapping(128, 16, 0.950f, 0.950f, &Kaiser10), /* Q7 */  /* 93.1% cutoff (~100 dB stop) 10 */
749     QualityMapping(160, 16, 0.960f, 0.960f, &Kaiser10), /* Q8 */  /* 94.5% cutoff (~100 dB stop) 10 */
750     QualityMapping(192, 32, 0.968f, 0.968f, &Kaiser12), /* Q9 */  /* 95.5% cutoff (~100 dB stop) 10 */
751     QualityMapping(256, 32, 0.975f, 0.975f, &Kaiser12), /* Q10 */ /* 96.6% cutoff (~100 dB stop) 10 */
752 ];
753 
754 
755 nothrow @trusted @nogc:
756 /*8, 24, 40, 56, 80, 104, 128, 160, 200, 256, 320*/
757 double computeFunc (float x, immutable FuncDef* func) 
758 {
759     version(Posix) import core.stdc.math : lrintf;
760     import std.math : floor;
761     //double[4] interp;
762     float y = x*func.oversample;
763     version(Posix) {
764     int ind = cast(int)lrintf(floor(y));
765     } else {
766     int ind = cast(int)(floor(y));
767     }
768     float frac = (y-ind);
769     immutable f2 = frac*frac;
770     immutable f3 = f2*frac;
771     double interp3 = -0.1666666667*frac+0.1666666667*(f3);
772     double interp2 = frac+0.5*(f2)-0.5*(f3);
773     //double interp2 = 1.0f-0.5f*frac-f2+0.5f*f3;
774     double interp0 = -0.3333333333*frac+0.5*(f2)-0.1666666667*(f3);
775     // just to make sure we don't have rounding problems
776     double interp1 = 1.0f-interp3-interp2-interp0;
777     //sum = frac*accum[1]+(1-frac)*accum[2];
778     return interp0*func.table[ind]+interp1*func.table[ind+1]+interp2*func.table[ind+2]+interp3*func.table[ind+3];
779 }
780 
781 
782 // the slow way of computing a sinc for the table; should improve that some day
783 float sinc (float cutoff, float x, int N, immutable FuncDef *windowFunc) 
784 {
785     version(LittleEndian) {
786     align(1) union temp_float { align(1): float f; uint n; }
787     } else {
788     static T fabs(T) (T n) pure { static if (__VERSION__ > 2067) pragma(inline, true); return (n < 0 ? -n : n); }
789     }
790     import std.math : sin, PI;
791     version(LittleEndian) {
792     temp_float txx = void;
793     txx.f = x;
794     txx.n &= 0x7fff_ffff; // abs
795     if (txx.f < 1.0e-6f) return cutoff;
796     if (txx.f > 0.5f*N) return 0;
797     } else {
798     if (fabs(x) < 1.0e-6f) return cutoff;
799     if (fabs(x) > 0.5f*N) return 0;
800     }
801     //FIXME: can it really be any slower than this?
802     immutable float xx = x*cutoff;
803     immutable pixx = PI*xx;
804     version(LittleEndian) {
805     return cutoff*sin(pixx)/pixx*computeFunc(2.0*txx.f/N, windowFunc);
806     } else {
807     return cutoff*sin(pixx)/pixx*computeFunc(fabs(2.0*x/N), windowFunc);
808     }
809 }
810 
811 
812 void cubicCoef (in float frac, float* interp) {
813     immutable f2 = frac*frac;
814     immutable f3 = f2*frac;
815     // compute interpolation coefficients; i'm not sure whether this corresponds to cubic interpolation but I know it's MMSE-optimal on a sinc
816     interp[0] =  -0.16667f*frac+0.16667f*f3;
817     interp[1] = frac+0.5f*f2-0.5f*f3;
818     //interp[2] = 1.0f-0.5f*frac-f2+0.5f*f3;
819     interp[3] = -0.33333f*frac+0.5f*f2-0.16667f*f3;
820     // just to make sure we don't have rounding problems
821     interp[2] = 1.0-interp[0]-interp[1]-interp[3];
822 }
823 
824 
825 // ////////////////////////////////////////////////////////////////////////// //
826 int resamplerBasicDirect(T) (ref SpeexResampler st, uint chanIdx, const(float)* indata, uint* indataLen, float* outdata, uint* outdataLen)
827 if (is(T == float) || is(T == double))
828 {
829     auto N = st.filterLen;
830     static if (is(T == double)) assert(N%4 == 0);
831     int outSample = 0;
832     int lastSample = st.lastSample.ptr[chanIdx];
833     uint sampFracNum = st.sampFracNum.ptr[chanIdx];
834     const(float)* sincTable = st.sincTable;
835     immutable outStride = st.outStride;
836     immutable intAdvance = st.intAdvance;
837     immutable fracAdvance = st.fracAdvance;
838     immutable denRate = st.denRate;
839     T sum = void;
840     while (!(lastSample >= cast(int)(*indataLen) || outSample >= cast(int)(*outdataLen))) {
841     const(float)* sinct = &sincTable[sampFracNum*N];
842     const(float)* iptr = &indata[lastSample];
843     static if (is(T == float)) {
844         // at least 2x speedup with SSE here (but for unrolled loop)
845         if (N%4 == 0) {
846     version(sincresample_use_sse) {
847         //align(64) __gshared float[4] zero = 0;
848         align(64) __gshared float[4+128] zeroesBuf = 0; // dmd cannot into such aligns, alas
849         __gshared uint zeroesptr = 0;
850         if (zeroesptr == 0) {
851         zeroesptr = cast(uint)zeroesBuf.ptr;
852         if (zeroesptr&0x3f) zeroesptr = (zeroesptr|0x3f)+1;
853         }
854         //assert((zeroesptr&0x3f) == 0, "wtf?!");
855         asm nothrow @safe @nogc {
856         mov       ECX,[N];
857         shr       ECX,2;
858         mov       EAX,[zeroesptr];
859         movaps    XMM0,[EAX];
860         mov       EAX,[sinct];
861         mov       EBX,[iptr];
862         mov       EDX,16;
863         align 8;
864         rbdseeloop:
865         movups    XMM1,[EAX];
866         movups    XMM2,[EBX];
867         mulps     XMM1,XMM2;
868         addps     XMM0,XMM1;
869         add       EAX,EDX;
870         add       EBX,EDX;
871         dec       ECX;
872         jnz       rbdseeloop;
873         // store result in sum
874         movhlps   XMM1,XMM0; // now low part of XMM1 contains high part of XMM0
875         addps     XMM0,XMM1; // low part of XMM0 is ok
876         movaps    XMM1,XMM0;
877         shufps    XMM1,XMM0,0b_01_01_01_01; // 2nd float of XMM0 goes to the 1st float of XMM1
878         addss     XMM0,XMM1;
879         movss     [sum],XMM0;
880         }
881         /*
882         float sum1 = 0;
883         foreach (immutable j; 0..N) sum1 += sinct[j]*iptr[j];
884         import std.math;
885         if (fabs(sum-sum1) > 0.000001f) {
886         import core.stdc.stdio;
887         printf("sum=%f; sum1=%f\n", sum, sum1);
888         assert(0);
889         }
890         */
891     } else {
892         // no SSE; for my i3 unrolled loop is almost of the speed of SSE code
893         T[4] accum = 0;
894         foreach (immutable j; 0..N/4) {
895         accum.ptr[0] += *sinct++ * *iptr++;
896         accum.ptr[1] += *sinct++ * *iptr++;
897         accum.ptr[2] += *sinct++ * *iptr++;
898         accum.ptr[3] += *sinct++ * *iptr++;
899         }
900         sum = accum.ptr[0]+accum.ptr[1]+accum.ptr[2]+accum.ptr[3];
901     }
902         } else {
903     sum = 0;
904     foreach (immutable j; 0..N) sum += *sinct++ * *iptr++;
905         }
906         outdata[outStride*outSample++] = sum;
907     } else {
908         if (N%4 == 0) {
909     //TODO: write SSE code here!
910     // for my i3 unrolled loop is ~2 times faster
911     T[4] accum = 0;
912     foreach (immutable j; 0..N/4) {
913         accum.ptr[0] += cast(double)*sinct++ * cast(double)*iptr++;
914         accum.ptr[1] += cast(double)*sinct++ * cast(double)*iptr++;
915         accum.ptr[2] += cast(double)*sinct++ * cast(double)*iptr++;
916         accum.ptr[3] += cast(double)*sinct++ * cast(double)*iptr++;
917     }
918     sum = accum.ptr[0]+accum.ptr[1]+accum.ptr[2]+accum.ptr[3];
919         } else {
920     sum = 0;
921     foreach (immutable j; 0..N) sum += cast(double)*sinct++ * cast(double)*iptr++;
922         }
923         outdata[outStride*outSample++] = cast(float)sum;
924     }
925     lastSample += intAdvance;
926     sampFracNum += fracAdvance;
927     if (sampFracNum >= denRate) {
928         sampFracNum -= denRate;
929         ++lastSample;
930     }
931     }
932     st.lastSample.ptr[chanIdx] = lastSample;
933     st.sampFracNum.ptr[chanIdx] = sampFracNum;
934     return outSample;
935 }
936 
937 
938 int resamplerBasicInterpolate(T) (ref SpeexResampler st, uint chanIdx, const(float)* indata, uint *indataLen, float *outdata, uint *outdataLen)
939 if (is(T == float) || is(T == double))
940 {
941     immutable N = st.filterLen;
942     assert(N%4 == 0);
943     int outSample = 0;
944     int lastSample = st.lastSample.ptr[chanIdx];
945     uint sampFracNum = st.sampFracNum.ptr[chanIdx];
946     immutable outStride = st.outStride;
947     immutable intAdvance = st.intAdvance;
948     immutable fracAdvance = st.fracAdvance;
949     immutable denRate = st.denRate;
950     float sum;
951 
952     float[4] interp = void;
953     T[4] accum = void;
954     while (!(lastSample >= cast(int)(*indataLen) || outSample >= cast(int)(*outdataLen))) {
955     const(float)* iptr = &indata[lastSample];
956     const int offset = sampFracNum*st.oversample/st.denRate;
957     const float frac = (cast(float)((sampFracNum*st.oversample)%st.denRate))/st.denRate;
958     accum[] = 0;
959     //TODO: optimize!
960     foreach (immutable j; 0..N) {
961         immutable T currIn = iptr[j];
962         accum.ptr[0] += currIn*(st.sincTable[4+(j+1)*st.oversample-offset-2]);
963         accum.ptr[1] += currIn*(st.sincTable[4+(j+1)*st.oversample-offset-1]);
964         accum.ptr[2] += currIn*(st.sincTable[4+(j+1)*st.oversample-offset]);
965         accum.ptr[3] += currIn*(st.sincTable[4+(j+1)*st.oversample-offset+1]);
966     }
967 
968     cubicCoef(frac, interp.ptr);
969     sum = (interp.ptr[0]*accum.ptr[0])+(interp.ptr[1]*accum.ptr[1])+(interp.ptr[2]*accum.ptr[2])+(interp.ptr[3]*accum.ptr[3]);
970 
971     outdata[outStride*outSample++] = sum;
972     lastSample += intAdvance;
973     sampFracNum += fracAdvance;
974     if (sampFracNum >= denRate) {
975         sampFracNum -= denRate;
976         ++lastSample;
977     }
978     }
979 
980     st.lastSample.ptr[chanIdx] = lastSample;
981     st.sampFracNum.ptr[chanIdx] = sampFracNum;
982     return outSample;
983 }
984 
985 
986 // ////////////////////////////////////////////////////////////////////////// //
987 uint gcd (uint a, uint b) pure {
988     if (a == 0) return b;
989     if (b == 0) return a;
990     for (;;) {
991     if (a > b) {
992         a %= b;
993         if (a == 0) return b;
994         if (a == 1) return 1;
995     } else {
996         b %= a;
997         if (b == 0) return a;
998         if (b == 1) return 1;
999     }
1000     }
1001 }
1002 
1003 
1004 // ////////////////////////////////////////////////////////////////////////// //
1005 // very simple and cheap cubic upsampler
1006 struct CubicUpsampler 
1007 {
1008 public:
1009 nothrow @trusted @nogc:
1010     float[2] curposfrac; // current position offset [0..1)
1011     float step; // how long we should move on one step?
1012     float[4][2] data; // -1..3
1013     uint[2] drain;
1014 
1015     void reset () 
1016     {
1017         curposfrac[] = 0.0f;
1018         foreach (ref d; data) d[] = 0.0f;
1019         drain[] = 0;
1020     }
1021 
1022     bool setup (float astep) 
1023     {
1024         if (astep >= 1.0f) return false;
1025         step = astep;
1026         return true;
1027     }
1028 
1029     /*
1030     static struct Data {
1031     const(float)[] dataIn;
1032     float[] dataOut;
1033     uint inputSamplesUsed; // out value, in samples (i.e. multiplied by channel count)
1034     uint outputSamplesUsed; // out value, in samples (i.e. multiplied by channel count)
1035     }
1036     */
1037 
1038     SpeexResampler.Error process (ref SpeexResampler.Data d) 
1039     {
1040         d.inputSamplesUsed = d.outputSamplesUsed = 0;
1041         if (d.dataOut.length < 2) return SpeexResampler.Error.OK;
1042         foreach (uint cidx; 0..2) {
1043             uint inleft = cast(uint)d.dataIn.length/2;
1044             uint outleft = cast(uint)d.dataOut.length/2;
1045             processChannel(inleft, outleft, (d.dataIn.length ? d.dataIn.ptr+cidx : null), (d.dataOut.length ? d.dataOut.ptr+cidx : null), cidx);
1046             d.outputSamplesUsed += cast(uint)(d.dataOut.length/2)-outleft;
1047             d.inputSamplesUsed += cast(uint)(d.dataIn.length/2)-inleft;
1048         }
1049         return SpeexResampler.Error.OK;
1050     }
1051 
1052     private void processChannel (ref uint inleft, ref uint outleft, const(float)* dataIn, float* dataOut, uint cidx) 
1053     {
1054         if (outleft == 0) return;
1055         if (inleft == 0 && drain.ptr[cidx] <= 1) return;
1056         auto dt = data.ptr[cidx].ptr;
1057         auto drn = drain.ptr+cidx;
1058         auto cpf = curposfrac.ptr+cidx;
1059         immutable float st = step;
1060         for (;;) {
1061             // fill buffer
1062             while ((*drn) < 4) {
1063         if (inleft == 0) return;
1064         dt[(*drn)++] = *dataIn;
1065         dataIn += 2;
1066         --inleft;
1067             }
1068             if (outleft == 0) return;
1069             --outleft;
1070             // cubic interpolation
1071             /*version(none)*/ {
1072         // interpolate between y1 and y2
1073         immutable float mu = (*cpf); // how far we are moved from y1 to y2
1074         immutable float mu2 = mu*mu; // wow
1075         immutable float y0 = dt[0], y1 = dt[1], y2 = dt[2], y3 = dt[3];
1076         version(complex_cubic) {
1077             immutable float z0 = 0.5*y3;
1078             immutable float z1 = 0.5*y0;
1079             immutable float a0 = 1.5*y1-z1-1.5*y2+z0;
1080             immutable float a1 = y0-2.5*y1+2*y2-z0;
1081             immutable float a2 = 0.5*y2-z1;
1082         } else {
1083             immutable float a0 = y3-y2-y0+y1;
1084             immutable float a1 = y0-y1-a0;
1085             immutable float a2 = y2-y0;
1086         }
1087         *dataOut = a0*mu*mu2+a1*mu2+a2*mu+y1;
1088             }// else *dataOut = dt[1];
1089             dataOut += 2;
1090             if (((*cpf) += st) >= 1.0f) 
1091             {
1092                 (*cpf) -= 1.0f;
1093                 dt[0] = dt[1];
1094                 dt[1] = dt[2];
1095                 dt[2] = dt[3];
1096                 dt[3] = 0.0f;
1097                 --(*drn); // will request more input bytes
1098             }
1099         }
1100     }
1101 }
1102 
1103 
1104 interface SampleController {
1105 	/++
1106 		Pauses playback, keeping its position. Use [resume] to pick up where it left off.
1107 	+/
1108 	void pause();
1109 	/++
1110 		Resumes playback after a call to [pause].
1111 	+/
1112 	void resume();
1113 	/++
1114 		Stops playback. Once stopped, it cannot be restarted
1115 		except by creating a new sample from the [AudioOutputThread]
1116 		object.
1117 	+/
1118 	void stop();
1119 	/++
1120 		Reports the current stream position, in seconds, if available (NaN if not).
1121 	+/
1122 	float position();
1123 
1124 	/++
1125 		If the sample has finished playing. Happens when it runs out or if it is stopped.
1126 	+/
1127 	bool finished();
1128 
1129 	/++
1130 		If the sample has been paused.
1131 		History:
1132 			Added May 26, 2021 (dub v10.0)
1133 	+/
1134 	bool paused();
1135 }
1136 
1137 package class SampleControlFlags : SampleController 
1138 {
1139 	void pause() { paused_ = true; }
1140 	void resume() { paused_ = false; }
1141 	void stop() { paused_ = false; stopped = true; }
1142 
1143 	bool paused_;
1144 	bool stopped;
1145 	bool finished_;
1146 
1147 	float position() { return currentPosition; }
1148 	bool finished() { return finished_; }
1149 	bool paused() { return paused_; }
1150 
1151 	float currentPosition = 0.0;
1152 }
1153 
1154 
1155 abstract class ResamplingContext {
1156 	int inputSampleRate;
1157 	int outputSampleRate;
1158 
1159 	int inputChannels;
1160 	int outputChannels;
1161 
1162 	SpeexResampler resamplerLeft;
1163 	SpeexResampler resamplerRight;
1164 
1165 	SpeexResampler.Data resamplerDataLeft;
1166 	SpeexResampler.Data resamplerDataRight;
1167 
1168 	float[][2] buffersIn;
1169 	float[][2] buffersOut;
1170 
1171 	uint rateNum;
1172 	uint rateDem;
1173 
1174 	float[][2] dataReady;
1175 
1176 	SampleControlFlags scflags;
1177 
1178 	this(SampleControlFlags scflags, int inputSampleRate, int outputSampleRate, int inputChannels, int outputChannels) {
1179 		this.scflags = scflags;
1180 		this.inputSampleRate = inputSampleRate;
1181 		this.outputSampleRate = outputSampleRate;
1182 		this.inputChannels = inputChannels;
1183 		this.outputChannels = outputChannels;
1184 
1185 
1186 		if(auto err = resamplerLeft.setup(1, inputSampleRate, outputSampleRate, 5))
1187 			throw new Exception("ugh");
1188 		resamplerRight.setup(1, inputSampleRate, outputSampleRate, 5);
1189 
1190 		resamplerLeft.getRatio(rateNum, rateDem);
1191 
1192 		int add = (rateNum % rateDem) ? 1 : 0;
1193 
1194 		buffersIn[0] = new float[](BUFFER_SIZE_FRAMES * rateNum / rateDem + add);
1195 		buffersOut[0] = new float[](BUFFER_SIZE_FRAMES);
1196 		if(inputChannels > 1) {
1197 			buffersIn[1] = new float[](BUFFER_SIZE_FRAMES * rateNum / rateDem + add);
1198 			buffersOut[1] = new float[](BUFFER_SIZE_FRAMES);
1199 		}
1200 	}
1201 
1202 	/+
1203 		float*[2] tmp;
1204 		tmp[0] = buffersIn[0].ptr;
1205 		tmp[1] = buffersIn[1].ptr;
1206 		auto actuallyGot = v.getSamplesFloat(v.chans, tmp.ptr, cast(int) buffersIn[0].length);
1207 		resamplerDataLeft.dataIn should be a slice of buffersIn[0] that is filled up
1208 		ditto for resamplerDataRight if the source has two channels
1209 	+/
1210 	abstract void loadMoreSamples();
1211 
1212 	bool loadMore() {
1213 		resamplerDataLeft.dataIn = buffersIn[0];
1214 		resamplerDataLeft.dataOut = buffersOut[0];
1215 
1216 		resamplerDataRight.dataIn = buffersIn[1];
1217 		resamplerDataRight.dataOut = buffersOut[1];
1218 
1219 		loadMoreSamples();
1220 
1221 		//resamplerLeft.reset();
1222 
1223 		if(auto err = resamplerLeft.process(resamplerDataLeft))
1224 			throw new Exception("ugh");
1225 		if(inputChannels > 1)
1226 			//resamplerRight.reset();
1227 			resamplerRight.process(resamplerDataRight);
1228 
1229 		resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[0 .. resamplerDataLeft.outputSamplesUsed];
1230 		resamplerDataRight.dataOut = resamplerDataRight.dataOut[0 .. resamplerDataRight.outputSamplesUsed];
1231 
1232 		if(resamplerDataLeft.dataOut.length == 0) {
1233 			return true;
1234 		}
1235 		return false;
1236 	}
1237 
1238 
1239 	bool fillBuffer(short[] buffer) {
1240 		if(cast(int) buffer.length != buffer.length)
1241 			throw new Exception("eeeek");
1242 
1243 		if(scflags.paused) {
1244 			buffer[] = 0;
1245 			return true;
1246 		}
1247 
1248 		if(outputChannels == 1) {
1249 			foreach(ref s; buffer) {
1250 				if(resamplerDataLeft.dataOut.length == 0) {
1251 					if(loadMore()) {
1252 						scflags.finished_ = true;
1253 						return false;
1254 					}
1255 				}
1256 
1257 				if(inputChannels == 1) {
1258 					s = cast(short) (resamplerDataLeft.dataOut[0] * short.max);
1259 					resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $];
1260 				} else {
1261 					s = cast(short) ((resamplerDataLeft.dataOut[0] + resamplerDataRight.dataOut[0]) * short.max / 2);
1262 
1263 					resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $];
1264 					resamplerDataRight.dataOut = resamplerDataRight.dataOut[1 .. $];
1265 				}
1266 			}
1267 
1268 			scflags.currentPosition += cast(float) buffer.length / outputSampleRate / outputChannels;
1269 		} else if(outputChannels == 2) {
1270 			foreach(idx, ref s; buffer) {
1271 				if(resamplerDataLeft.dataOut.length == 0) {
1272 					if(loadMore()) {
1273 						scflags.finished_ = true;
1274 						return false;
1275 					}
1276 				}
1277 
1278 				if(inputChannels == 1) {
1279 					s = cast(short) (resamplerDataLeft.dataOut[0] * short.max);
1280 					if(idx & 1)
1281 						resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $];
1282 				} else {
1283 					if(idx & 1) {
1284 						s = cast(short) (resamplerDataRight.dataOut[0] * short.max);
1285 						resamplerDataRight.dataOut = resamplerDataRight.dataOut[1 .. $];
1286 					} else {
1287 						s = cast(short) (resamplerDataLeft.dataOut[0] * short.max);
1288 						resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $];
1289 					}
1290 				}
1291 			}
1292 
1293 			scflags.currentPosition += cast(float) buffer.length / outputSampleRate / outputChannels;
1294 		} else assert(0);
1295 
1296 		if(scflags.stopped)
1297 			scflags.finished_ = true;
1298 		return !scflags.stopped;
1299 	}
1300 
1301     bool fillBuffer(float[] buffer) {
1302 		if(cast(int) buffer.length != buffer.length)
1303 			throw new Exception("eeeek");
1304 
1305 		if(scflags.paused) {
1306 			buffer[] = 0;
1307 			return true;
1308 		}
1309 
1310 		if(outputChannels == 1) 
1311         {
1312 			foreach(ref s; buffer) 
1313             {
1314 				if(resamplerDataLeft.dataOut.length == 0) 
1315                 {
1316 					if(loadMore()) 
1317                     {
1318 						scflags.finished_ = true;
1319 						return false;
1320 					}
1321 				}
1322 
1323 				if(inputChannels == 1) 
1324                 {
1325 					s = resamplerDataLeft.dataOut[0];
1326 					resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $];
1327 				} 
1328                 else 
1329                 {
1330 					s = (resamplerDataLeft.dataOut[0] + resamplerDataRight.dataOut[0]) / 2;
1331 
1332 					resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $];
1333 					resamplerDataRight.dataOut = resamplerDataRight.dataOut[1 .. $];
1334 				}
1335 			}
1336 
1337 			scflags.currentPosition += cast(float) buffer.length / outputSampleRate / outputChannels;
1338 		}
1339         else if(outputChannels == 2) 
1340         {
1341 			foreach(idx, ref s; buffer) 
1342             {
1343 				if(resamplerDataLeft.dataOut.length == 0) 
1344                 {
1345 					if(loadMore()) 
1346                     {
1347 						scflags.finished_ = true;
1348 						return false;
1349 					}
1350 				}
1351 
1352 				if(inputChannels == 1) 
1353                 {
1354 					s = resamplerDataLeft.dataOut[0];
1355 					if(idx & 1)
1356 						resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $];
1357                 } 
1358                 else 
1359                 {
1360 					if(idx & 1) 
1361                     {
1362 						s = resamplerDataRight.dataOut[0];
1363 						resamplerDataRight.dataOut = resamplerDataRight.dataOut[1 .. $];
1364 					} 
1365                     else 
1366                     {
1367 						s = resamplerDataLeft.dataOut[0];
1368 						resamplerDataLeft.dataOut = resamplerDataLeft.dataOut[1 .. $];
1369 					}
1370 				}
1371 			}
1372 
1373 			scflags.currentPosition += cast(float) buffer.length / outputSampleRate / outputChannels;
1374 		} else assert(0);
1375 
1376 		if(scflags.stopped)
1377 			scflags.finished_ = true;
1378 		return !scflags.stopped;
1379 	}
1380 }